主要的身份驗證部分在前兩天已經寫完了,這篇要分享的是我在過程中遇到的問題以及解決方式。
這是驗證信預設的內容:

如果想要修改的話,可以到 Amazon Cognito -> User pools -> Messaging 最底下有個 Message templates 找到 Verification message template 編輯即可



當使用 Auth.signIn 登入後關閉 App 重啟,Auth.currentAuthenticatedUser() 會回傳 The user is not authenticated,它不會記住關閉 App 前的登入狀態。
尋找解法的時候我看到了這個 issue,發文的人和我的疑問是一樣的,下面有回覆說是因為 Amplify 所使用的 AsyncStorage 過舊,新版本已經不再支持,所以重新覆寫一下 Storage 的部分即可:
// services/storage.ts
import AsyncStorage from '@react-native-async-storage/async-storage'
/*
 * Copyright 2017-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with
 * the License. A copy of the License is located at
 *
 *     http://aws.amazon.com/apache2.0/
 *
 * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
 * and limitations under the License.
 */
const MEMORY_KEY_PREFIX = '@StorageService:'
let dataMemory: any = {}
/** @class */
export class MemoryStorageNew {
  static syncPromise: Promise<any> | null = null
  /**
   * This is used to set a specific item in storage
   * @param {string} key - the key for the item
   * @param {object} value - the value
   * @returns {string} value that was set
   */
  static setItem(key: any, value: any) {
    AsyncStorage.setItem(MEMORY_KEY_PREFIX + key, value)
    dataMemory[key] = value
    return dataMemory[key]
  }
  /**
   * This is used to get a specific key from storage
   * @param {string} key - the key for the item
   * This is used to clear the storage
   * @returns {string} the data item
   */
  static getItem(key: string) {
    return Object.prototype.hasOwnProperty.call(dataMemory, key)
      ? dataMemory[key]
      : undefined
  }
  /**
   * This is used to remove an item from storage
   * @param {string} key - the key being set
   * @returns {string} value - value that was deleted
   */
  static removeItem(key: string) {
    AsyncStorage.removeItem(MEMORY_KEY_PREFIX + key)
    return delete dataMemory[key]
  }
  /**
   * This is used to clear the storage
   * @returns {string} nothing
   */
  static clear() {
    dataMemory = {}
    return dataMemory
  }
  /**
   * Will sync the MemoryStorage data from AsyncStorage to storageWindow MemoryStorage
   * @returns {void}
   */
  static sync() {
    if (!MemoryStorageNew.syncPromise) {
      MemoryStorageNew.syncPromise = new Promise((res, rej) => {
        AsyncStorage.getAllKeys((errKeys, keys) => {
          if (errKeys) rej(errKeys)
          const memoryKeys = keys!.filter(key =>
            key.startsWith(MEMORY_KEY_PREFIX)
          )
          AsyncStorage.multiGet(memoryKeys, (err, stores) => {
            if (err) rej(err)
            stores!.map((result, index, store) => {
              const key = store[index][0]
              const value = store[index][1]
              const memoryKey = key.replace(MEMORY_KEY_PREFIX, '')
              dataMemory[memoryKey] = value
            })
            res(true)
          })
        })
      })
    }
    return MemoryStorageNew.syncPromise
  }
}
/** @class */
export default class StorageHelper {
  private storageWindow: any
  /**
   * This is used to get a storage object
   * @returns {object} the storage
   */
  constructor() {
    this.storageWindow = MemoryStorageNew
  }
  /**
   * This is used to return the storage
   * @returns {object} the storage
   */
  getStorage() {
    return this.storageWindow
  }
}
在 Amplify.configure 設置 Auth.storage 為 MemoryStorageNew
import { MemoryStorageNew } from '@/services/storage'
import awsconfig from './aws-exports'
Amplify.configure({
  ...awsconfig,
  ssr: true,
  Auth: {
    storage: MemoryStorageNew,
  }
})
接續上面的繼續說,如果今天登入表單可以自行勾選是否要記住登入狀態,那就需要稍作調整,因為上面的例子是無論如何都記憶住登入狀態。
在網上查了很多資料,得到的都是 React 的解法,就是根據是否要記住登入狀態來修改 storage 為 localStorage 或者 sessionStorage,但是 RN 上無法這樣做。
所以我在調用 getUser 時傳入 isAutomatic 來判斷是不是應用剛啟用正在檢驗身份,如果是並且 rememberMe 為 true 或者調用 Auth.signIn 的話才要自動登入。
import AsyncStorage from '@react-native-async-storage/async-storage'
// ...
const [user, setUser] = useState<any>(null)
const [rememberMe, setRememberMe] = useState(false)
useEffect(() => {
    const unsubscribe = Hub.listen('auth', ({ payload: { event, data } }) => {
      switch (event) {
        case 'signIn':
          getUser(false)
          break
        case 'signOut':
          setUser(null)
          break
        case 'signIn_failure':
          console.log('Sign in failure', data)
          break
      }
    })
    getUser(true)
    return unsubscribe
}, [])
const getUser = async (isAutomatic: boolean) => {
    try {
      const rememberMe = await AsyncStorage.getItem('rememberMe')
      setRememberMe(rememberMe === 'true')
      // 勾選"記住我" 或使用 login function 登入
      const shouldKeepLoggedIn = isAutomatic && rememberMe === 'true'
      if (shouldKeepLoggedIn || !isAutomatic) {
        const currentUser = await Auth.currentAuthenticatedUser()
        setUser(currentUser)
      }
    } catch (error) {
      console.error(error)
      setUser(null)
    }
}
const login = async (data) => {
    try {
      const { email, password } = data
      await Auth.signIn(email, password)
      // 保存"記住我"的勾選結果
      if (rememberMe) {
        await AsyncStorage.setItem('rememberMe', 'true')
      } else {
        await AsyncStorage.setItem('rememberMe', 'false')
      }
    } catch (error: any) {
        console.log(error)
    }
}